﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using DeepZoomPivotConstructor.VisualLib;
using DeepZoomPivotConstructor.VisualLib.Hotspots;
using DeepZoomPivotConstructor.VisualLib.Utilities;
using IndicoInterface.SimpleAgendaDataModel;
using RenderEngineInterfaces;

namespace IndicoRender
{
    /// <summary>
    /// Render an indico session. Part of the rendering chain for doing
    /// an Indico meeting
    /// </summary>
    [Export(typeof(IRenderItem))]
    [RenderInfo(typeof(Session))]
    public class RenderIndicoSession : IRenderItem
    {

        /// <summary>
        /// So we can do a render or two...
        /// </summary>
        [Import(typeof(IRenderEngine))]
        private IRenderEngine RenderMe { get; set; }

        /// <summary>
        /// Helps with laying out everyone!
        /// </summary>
        [Import]
        private ILayoutHelper LayMeOut { get; set; }

        /// <summary>
        /// Do the rendering - we actually just create a file info and then get
        /// someone to try to render that. The "new" operator is fast enough that
        /// we don't need to take a hit on the effort of creating a task.
        /// [We also know the path exists as a file on the OS as we've checked
        /// it below - if that changes out from under us... well... :-)]
        /// </summary>
        /// <param name="what"></param>
        /// <returns></returns>
        public Task<Visual> Render(object what)
        {
            return Task<Visual>.Factory.StartNew(() =>
                {
                    return RenderASession(what as Session);
                });
        }

        /// <summary>
        /// If we should be using a different temp directory than the one that is there by default... :-)
        /// </summary>
        [Import("RenderingTempDir", AllowDefault = true)]
#pragma warning disable 0649
        private DirectoryInfo RenderingTempDir;
#pragma warning restore 0649

        /// <summary>
        /// Does the actual render (often on a different thread!)
        /// </summary>
        /// <param name="session"></param>
        /// <returns></returns>
        private Visual RenderASession(Session session)
        {
            if (session == null)
            {
                return null;
            }

            if ((session.Talks == null || session.Talks.Length == 0)
                && (session.SessionMaterial == null || session.SessionMaterial.Length == 0))
            {
                return null;
            }

            ///
            /// Render all the talks for material as well as regular talks. And those that made it we
            /// should add titles to! That done, add some basic navigation.
            /// 

            var realRenderedTalks = RenderAgendaTalkToVisuals(session.Talks);
            var realRenderedMaterial = RenderAgendaTalkToVisuals(session.SessionMaterial);

            VisualHolder sessionHolder = new VisualHolder();

            var renderedTalksNavigation = (from r in new VisualSeqenceWrapper(realRenderedMaterial.Concat(realRenderedTalks), sessionHolder) { EachVisualShouldCenterOnClick = false }
                                           select r).ToArray();

            if (renderedTalksNavigation.Length == 0)
            {
                return null;
            }

            ///
            /// Now we do the talk layout.
            /// 

            var sessionVisual = LayMeOut.LayoutIn2By3Rows(renderedTalksNavigation);
            sessionHolder.AddImage(sessionVisual);

            VisualHotSpot vhs = sessionHolder.CreateVHS();
            vhs.Add(new HSActionCenterThere(renderedTalksNavigation[0]) { KeysToActivate = HotSpotActionBase.ActivationKeys.KeyDownArrow });
            sessionHolder.AddImage(vhs);

            ///
            /// Add a title, etc., onto this guy!
            /// 

            sessionHolder.Info = new Visual.VisualInfo()
            {
                Title = session.Title,
                Info = new string[] { IndicoRenderUtilities.TimeSpanToString(session.StartDate, session.EndDate) }
            };

            return IndicoRenderUtilities.getSizeRight(sessionHolder);
        }

        /// <summary>
        /// Given a sequence of talks, render it to visuals, with titles.
        /// </summary>
        /// <param name="session"></param>
        /// <returns></returns>
        private IEnumerable<Visual> RenderAgendaTalkToVisuals(IEnumerable<Talk> talks)
        {
            if (talks == null)
            {
                return Enumerable.Empty<Visual>();
            }
            var renderedTalksFutures = from t in talks
                                       where (t.SlideURL != null) || (t.SubTalks != null && t.SubTalks.Length > 0)
                                       select new
                                       {
                                           render = RenderMe.RenderToFuture(t),
                                           talk = t
                                       };
            var realRenderedTalks = from tf in renderedTalksFutures.AsParallel()
                                    where tf.render != null && tf.render.Result != null
                                    let rt = IndicoRenderUtilities.getSizeRight(tf.render.Result)
                                    select CreateTitleBlockForTalk(rt, tf.talk);
            return realRenderedTalks;
        }

        /// <summary>
        /// Create a title and background for this talk - and pretty up the information correctly!
        /// </summary>
        /// <param name="rt"></param>
        /// <param name="tf"></param>
        /// <returns></returns>
        private Visual CreateTitleBlockForTalk(Visual rt, Talk tf)
        {
            var result = new VisualTitleAndBackground(rt, RenderingTempDir);
            if (tf.SlideURL != null)
            {
                result.Add(0.5, rt.Info.Title, new HSActionLink(new Uri(tf.SlideURL)));
            }
            else
            {
                result.Add(0.5, rt.Info.Title);
            }
            result.Add(0.25, rt.Info.Info[0]);

            return result;
        }

        /// <summary>
        /// We can do anything that is passed to us.
        /// </summary>
        /// <param name="what"></param>
        /// <returns></returns>
        public RenderPluginPriority RenderPriority(object what)
        {
            return RenderPluginPriority.RPPGeneric;
        }
    }
}
